package license

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"gitee.com/zmhfly8/license/utils"
	"github.com/Gre-Z/common/jtime"
	"github.com/gin-gonic/gin"
	"github.com/shirou/gopsutil/v3/host"
	"net"
	"net/http"
	"strconv"
	"sync"
	"time"
)

type Client struct {
	managerURL    string
	key           string
	Secret        string
	ip            string
	machineCode   string
	checkInterval time.Duration
	valid         bool
	mutex         sync.RWMutex
	httpClient    *http.Client
	CurrentKey    string
}

const checkInterval = 1 * time.Hour

// const checkInterval  = 30*time.Second
func NewClient(managerURL, key string, secret string) (*Client, error) {
	ip, err := getLocalIP()
	if err != nil {
		return nil, fmt.Errorf("failed to get IP: %w", err)
	}

	machineCode, err := getMachineCode()
	if err != nil {
		return nil, fmt.Errorf("failed to get machine code: %w", err)
	}

	c := &Client{
		managerURL:    managerURL,
		key:           key,
		Secret:        secret,
		ip:            ip,
		machineCode:   machineCode,
		checkInterval: checkInterval,
		CurrentKey:    secret,
		httpClient:    &http.Client{Timeout: 10 * time.Second},
	}

	go c.startChecker()
	c.checkAuthorization() // Initial check immediately

	return c, nil
}

func getLocalIP() (string, error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return "", err
	}

	for _, addr := range addrs {
		ipNet, ok := addr.(*net.IPNet)
		if ok && !ipNet.IP.IsLoopback() && ipNet.IP.To4() != nil {
			return ipNet.IP.String(), nil
		}
	}

	return "", errors.New("no valid IP address found")
}

func getMachineCode() (string, error) {
	info, err := host.Info()
	if err != nil {
		return "", err
	}
	return info.HostID, nil
}

func (c *Client) startChecker() {
	ticker := time.NewTicker(c.checkInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			c.checkAuthorization()
		}
	}
}

func (c *Client) checkAuthorization() {
	type req struct {
		Key         string `json:"key"`
		Ip          string `json:"ip"`
		MachineCode string `json:"machineCode"`
		Timestamp   string `json:"timestamp"`
		Signature   string `json:"signature"`
	}
	r := req{
		Key:         c.key,
		Ip:          c.ip,
		MachineCode: c.machineCode,
		Timestamp:   strconv.FormatInt(time.Now().Unix(), 10),
	}
	jsonBody, _ := json.Marshal(r)
	r.Signature = utils.ComputeHMAC(jsonBody, []byte(c.Secret))
	newBody, _ := json.Marshal(r)
	fmt.Println("开始请求", r)
	fmt.Println("开始请求", r.Ip)
	fmt.Println("开始请求", r.Timestamp)
	fmt.Println("开始请求", r.MachineCode)
	fmt.Println("开始请求", r.Signature)
	resp, err := c.httpClient.Post(
		c.managerURL+"/api/validate",
		"application/json",
		bytes.NewBuffer(newBody),
	)
	fmt.Println(err)
	fmt.Println("请求结果", resp)

	if err != nil {
		c.setValid(false, c.Secret)
		return
	}
	defer resp.Body.Close()

	var result struct {
		Valid    bool   `json:"valid"`
		Key      string `json:"key"`
		ValidMsg string `json:"validMsg"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		c.setValid(false, c.Secret)
		return
	}
	fmt.Println("请求结果", result.Valid)
	c.setValid(result.Valid, result.Key)
}

func (c *Client) setValid(valid bool, key string) {
	c.mutex.Lock()
	defer c.mutex.Unlock()
	c.valid = valid
	c.CurrentKey = key
}

func (c *Client) Middleware() gin.HandlerFunc {
	return func(ctx *gin.Context) {
		c.mutex.RLock()
		valid := c.valid
		c.mutex.RUnlock()
		type Result struct {
			Errno    int    `json:"errno"`
			Error    string `json:"error"`
			DataType string `json:"dataType"`
			Data     any    `json:"data"`
			Time     jtime.JsonTime
		}
		if !valid {
			ctx.JSON(http.StatusOK, Result{
				Errno:    403,
				Error:    "服务未授权，请联系管理员",
				DataType: "error",
				Data:     nil,
				Time: jtime.JsonTime{
					Time: time.Now(),
				},
			})
			//ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{
			//	"error": "服务未授权，请联系管理员",
			//})
			return
		}
		ctx.Next()
	}
}

func (c *Client) GetStatus() bool {
	c.mutex.RLock()
	defer c.mutex.RUnlock()
	return c.valid
}
